package cn.mjnxin.iot.connector.context;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import cn.mjnxin.iot.base.cache.LocalCache;
import cn.mjnxin.iot.base.log.LogWorker;
import cn.mjnxin.iot.connector.exception.GateNotFoundException;

/**
 * 业务上下文的框架实现
 * @author CLG 2023年10月10日
 * @version V1.0.0
 */
public class ConnectorContext implements IConnectorContext, IPeerContext{
	public static String                                       CONNECTOR_NAME         = "前置"; // 业务连接端名称
	public static String                                       PEER_NAME              = "终端"; // 业务连接端名称
	public static String                                       CONNECT_TYPE           = "HOST"; // 业务连接端名称
    /**
     * 当前处理线程的对端地址
     */
    private static final ThreadLocal<Object>                   currentPeerAddress     = new ThreadLocal<Object>();
    /**
     * 对端协议缓存<IP:PORT, pid>
     */
    private static final ConcurrentHashMap<String, Integer>    peerLinkMap            = new ConcurrentHashMap<>();
    /**
     * 对端数据缓存<IP:PORT, LocalCache>
     */
    private static final ConcurrentHashMap<String, LocalCache> peerDataMap            = new ConcurrentHashMap<>();
    /**
     * 对端连接数据缓存<LocalCache.toString|peerKey, LocalCache>
     */
    private static final ConcurrentHashMap<String, LocalCache> peerDataLinkMap        = new ConcurrentHashMap<>();
    
    /**
     * 对端标识缓存<peerKey, peer IP:Port>
     */
    private static final ConcurrentHashMap<String, String>     peerKeyMap             = new ConcurrentHashMap<>();

    /**
     * 上下文改变的监听器
     */
    private IPeerChangedListener                               contextChangedListener = null;
    
    /**
     * 对端网关地址关联<peer IPAddress:Port, gate IPAddress:Port>
     */
    private static final ConcurrentHashMap<String, String>     peerGateMap            = new ConcurrentHashMap<>();

    @Override
    public String currentPeerAddress() {
        return (String) currentPeerAddress.get();
    }

    @Override
    public void setCurrentPeerAddress(String peerAddress) {
        if (peerAddress == null) {
            currentPeerAddress.remove();
        }
        else {
            currentPeerAddress.set(peerAddress);
        }
    }

    public void setContextChangedListener(IPeerChangedListener contextChangedListener) {
        this.contextChangedListener = contextChangedListener;
    }
    
    /**
     * 获取终端地址列表
     * @return
     */
    public Collection<String> peerAddressList(){
        return Collections.unmodifiableCollection(peerLinkMap.keySet());
    }
    
    /**
     * 获取终端地址，PID列表
     * @return
     */
    public Map<String, Integer> peerLinkList(){
        return Collections.unmodifiableMap(peerLinkMap);
    }
    
	//第1步 设备加入
    @Override
    public void addPeer(String peerAddress, Integer pid, String gateAddress) {
		LogWorker.logForce(String.format("Peer[%d|%s] 新加入%s from Gate[%s]", pid, peerAddress, ConnectorContext.PEER_NAME, gateAddress));
        peerLinkMap.put(peerAddress, pid);
		if (peerGateMap.containsKey(peerAddress)) {
			return;
		}
		peerGateMap.put(peerAddress, gateAddress);
    }

    // 这里并不删除peerDataLinkMap的PeerKey缓存信息，留着后续重用
    @Override
    public void removePeer(String peerAddress) {
		LogWorker.logForce(String.format("Peer[%s] %s主动断连被踢出", peerAddress, ConnectorContext.PEER_NAME));
        Integer pid = peerLinkMap.remove(peerAddress);
        peerGateMap.remove(peerAddress);
        LocalCache cache = peerDataMap.remove(peerAddress);
        if (cache != null) {
            cache.delete(PEER_ADDRESS);
        }
        if (contextChangedListener != null) {
            contextChangedListener.onRemovePeer(pid, peerAddress, cache);
        }
    }

    @Override
    public void closePeer(String peerAddress) {
		LogWorker.logForce(String.format("Peer[%s] %s主动断连踢出%s", peerAddress, ConnectorContext.CONNECTOR_NAME, ConnectorContext.PEER_NAME));
        Integer pid = peerLinkMap.remove(peerAddress);
        peerGateMap.remove(peerAddress);
        LocalCache cache = peerDataMap.remove(peerAddress);
        if (cache != null) {
            cache.delete(PEER_ADDRESS);
        }
        if (contextChangedListener != null) {
            contextChangedListener.onClosePeer(pid, peerAddress, cache);
        }
    }

    // 这里并不删除peerDataLinkMap的PeerKey缓存信息，留着后续重用
    @Override
    public void unlinkPeer(String peerAddress, String peerKey) {
		LogWorker.logForce(String.format("Peer[%s] %s标记断连", peerAddress, ConnectorContext.PEER_NAME));
        Integer pid = peerLinkMap.get(peerAddress);
        LocalCache cache = peerDataMap.get(peerAddress);
        if (contextChangedListener != null) {
            contextChangedListener.onRemovePeer(pid, peerAddress, cache);
        }
    }

    // 这里并不删除peerDataLinkMap的PeerKey缓存信息，留着后续重用
    @Override
    public void kickoutPeer(String peerAddress) {
		LogWorker.logForce(String.format("Peer[%s] %s超时断连踢出", peerAddress, ConnectorContext.PEER_NAME));
        Integer pid = peerLinkMap.remove(peerAddress);
        peerGateMap.remove(peerAddress);
        LocalCache cache = peerDataMap.remove(peerAddress);
        if (cache != null) {
            cache.delete(PEER_ADDRESS);
        }
        if (contextChangedListener != null) {
            contextChangedListener.onKickoutPeer(pid, peerAddress, cache);
        }
    }

    //第一步 处理过程报文前的预处理
    @Override
    public void checkAndAddPeer(String peerAddress, Integer pid, String gateAddress) {
    	if (!peerLinkMap.containsKey(peerAddress)) {
    		LogWorker.logForce(String.format("Peer[%d|%s] %s中途加入 from Gate[%s]", pid, peerAddress, ConnectorContext.PEER_NAME, gateAddress));
            peerLinkMap.put(peerAddress, pid);
    		if (peerGateMap.containsKey(peerAddress)) {
    			return;
    		}
    		peerGateMap.put(peerAddress, gateAddress);
    	}
    }

	@Override
	public String getPeerAddress(String peerKey) {
    	if (peerDataLinkMap.containsKey(peerKey)) {
            return peerKeyMap.get(peerKey);
    	}
    	
    	return null;
	}

    //第二步 解析过程报文中非鉴权的处理
    /**
     * 业务数据是否存在
     */
    @Override
    public boolean isInLink(String peerAddress, String peerKey) {
        LocalCache cacheData = peerDataLinkMap.get(peerKey);
        if (cacheData != null) {
        	//切换，将新的peerAddress跟PeerKey绑定
            String oldAddress = (String) cacheData.get(PEER_ADDRESS);
        	//更换对端地址
            cacheData.add(PEER_ADDRESS, peerAddress);
        	if (peerLinkMap.containsKey(peerAddress) && !peerAddress.equals(oldAddress)) {
            	LogWorker.warn(String.format("New Peer[%s] old Peer[%s]", peerAddress, oldAddress));
                Integer pid = peerLinkMap.get(peerAddress);
                if (oldAddress != null) {
                	//清理旧数据
                    peerDataMap.remove(oldAddress); // 删除旧数据
                    peerLinkMap.remove(oldAddress); // 删除旧的连接缓存
                    String gateAddress = peerGateMap.remove(oldAddress);
                    if (gateAddress != null) {
                    	// 尽量保证报文能够发送出去
                		peerGateMap.put(peerAddress, gateAddress);
                    }
                }
//                peerDataMap.put(peerAddress, cacheData);
                setCurrentPeerAddress(peerAddress);
                if (contextChangedListener != null) {
                	//监控中间过程的链路切换
                    contextChangedListener.changePeerLink(pid, oldAddress, peerAddress, peerKey);
                }
            }
        }
        else { // 没有缓存数据则需要重新加载
        	if (peerLinkMap.containsKey(peerAddress)) {
        		return false;
        	}
        }
        //并发处理
        return peerDataLinkMap.containsKey(peerKey);
    }

    //第2步 解析过程报文中鉴权后的处理
    @Override
    public void addPeerData(String peerAddress, LocalCache cacheData) {
        // 如果没有现成的则判断是否peer断连更换了ip:port
        final String peerKey = cacheData.toString();
		if(peerDataLinkMap.containsKey(peerKey)) {
            LocalCache oldData = peerDataLinkMap.get(peerKey);
            if (oldData == null) {
            	peerKeyMap.put(peerKey, peerAddress);
                peerDataLinkMap.put(peerKey, cacheData);
                peerDataMap.put(peerAddress, cacheData);
                return;
            }
            
            String oldAddress = (String) oldData.get(PEER_ADDRESS); // 旧的Device IP:PORT
            oldData.concat(cacheData);
        	//更换对端地址
            oldData.add(PEER_ADDRESS, peerAddress);
            if (oldAddress != null && !oldAddress.equals(peerAddress)) {
            	LogWorker.warn(String.format("New Peer[%s] old Peer[%s]", peerAddress, oldAddress));
                peerDataMap.remove(oldAddress); // 删除旧数据
                Integer pid = peerLinkMap.remove(oldAddress); // 删除旧的连接缓存
                String gateAddress = peerGateMap.remove(oldAddress);
                if (gateAddress != null) {
                	// 尽量保证报文能够发送出去
            		peerGateMap.put(peerAddress, gateAddress);
                }
                if (contextChangedListener != null && pid != null) {
                    contextChangedListener.changePeerLink(pid, oldAddress, peerAddress, peerKey);
                }
            }
            if (peerAddress != null) {
                peerDataMap.put(peerAddress, oldData); // 增加新数据
            }
        }
        else {
        	peerKeyMap.put(peerKey, peerAddress);
            peerDataLinkMap.put(peerKey, cacheData);
            if (peerAddress != null) {
                peerDataMap.put(peerAddress, cacheData);
            }
        }
    }

    @Override
    public boolean isPeerCacheExists(String peerAddress) {
    	if (peerAddress == null || peerAddress.isEmpty()) {
    		return false;
    	}
        return peerDataMap.containsKey(peerAddress);
    }

    @Override
    public LocalCache getPeerCacheByAddress(String peerAddress) {
    	if (peerAddress == null || peerAddress.isEmpty()) {
    		return null;
    	}
    	if (peerDataMap.containsKey(peerAddress)) {
            return peerDataMap.get(peerAddress);	
    	}
    	
    	return null;
    }

    @Override
    public boolean isPeerLinkCacheExists(String peerKey) {
        return peerDataLinkMap.containsKey(peerKey);
    }

    @Override
    public LocalCache getPeerLinkCache(String peerKey) {
    	if (peerDataLinkMap.containsKey(peerKey)) {
            return peerDataLinkMap.get(peerKey);
    	}
    	
    	return null;
    }

    @Override
    public Collection<LocalCache> peerDataList() {
        return Collections.unmodifiableCollection(peerDataMap.values());
    }

    @Override
	public String getGateAddress(String peerAddress) {
		return getGateAddressByPeerAddress(peerAddress);
	}

	public static String getGateAddressByPeerAddress(String peerAddress) {
		if (!peerGateMap.containsKey(peerAddress)) {
        	LogWorker.error(String.format("Peer[%s] %s不存在", peerAddress, ConnectorContext.PEER_NAME));
			throw new GateNotFoundException("根据 PeerAddress：" + peerAddress + " 无法获取网关地址");
		}
		final String gateKey = peerGateMap.get(peerAddress);
		if (gateKey == null) {
        	LogWorker.error(String.format("Peer[%s] %s对应的 Gate：%s 不存在", peerAddress, ConnectorContext.PEER_NAME, gateKey));
			throw new GateNotFoundException("根据 PeerAddress：" + peerAddress + " 无法获取网关信息");
		}
		return gateKey;
    }

    @Override
    public void clearPeerData() {
        peerDataMap.clear();
        peerLinkMap.clear();
    	peerKeyMap.clear();
        peerDataLinkMap.clear();
        peerGateMap.clear();
    }
}
